package pso.position;

import java.util.logging.Logger;

import pso.particle.Particle;
import pso.particle.ParticleNeighborhood;
import evolution.problem.ProblemConstraints;

public class DefaultPositionUpdateStrategy implements PositionUpdateStrategy {

	private static final Logger log = Logger
			.getLogger(DefaultPositionUpdateStrategy.class.getCanonicalName());
	private ProblemConstraints problemConstraints;

	public DefaultPositionUpdateStrategy(ProblemConstraints problemConstraints) {
		this.problemConstraints = problemConstraints;
	}

	@Override
	public void updatePositions(Particle[] particles) {
		for (int i = 0; i < particles.length; i++) {
			Particle particle = particles[i];
			updateParticlePosition(particle);
		}
	}

	private void updateParticlePosition(Particle particle) {
		double[] position = particle.getChromosome();
		double[] newPosition = new double[position.length];
		// log.info("Old position: " + Arrays.toString(position));

		for (int d = 0; d < position.length; d++) {
			double velocity = particle.getVelocity()[d];
			double potentialPosition = position[d] + velocity;

			performUpdate(particle, newPosition, d, velocity, potentialPosition);
		}
		checkPosition(newPosition);
		particle.setPosition(newPosition);

		// log.info("New position: " + Arrays.toString(position));

		double fitness = particle.getFitness();
		if (fitness > particle.getBestValue()) {
			particle.setBestPosition(newPosition.clone());
			particle.setBestValue(fitness);
		}

		ParticleNeighborhood neighborhood = particle.getNeighborhood();
		if (fitness > neighborhood.getBestValue()) {
			neighborhood.setBestPosition(newPosition.clone());
			neighborhood.setBestValue(fitness);
		}
	}

	private void performUpdate(Particle particle, double[] newPosition,
			int dimension, double velocity, double potentialPosition) {
		if (potentialPosition > problemConstraints.getMaxArg()) {
			double maxArg = problemConstraints.getMaxArg();
			double diff = potentialPosition - maxArg;
			newPosition[dimension] = maxArg - diff;
			particle.getVelocity()[dimension] = -velocity;
			performUpdate(particle, newPosition, dimension, velocity,
					newPosition[dimension]);
		} else if (potentialPosition < problemConstraints.getMinArg()) {
			double minArg = problemConstraints.getMinArg();
			double diff = potentialPosition - minArg;
			newPosition[dimension] = minArg - diff;
			particle.getVelocity()[dimension] = -velocity;
			performUpdate(particle, newPosition, dimension, velocity,
					newPosition[dimension]);
		} else {
			newPosition[dimension] = potentialPosition;
		}
	}

	private void checkPosition(double[] newPosition) {
		for (double d : newPosition) {
			if (d > problemConstraints.getMaxArg()) {
				System.out.println(d);
			} else if (d < problemConstraints.getMinArg()) {
				System.out.println(d);
			}
		}
	}

	public ProblemConstraints getProblemConstraints() {
		return problemConstraints;
	}

	public void setProblemConstraints(ProblemConstraints problemConstraints) {
		this.problemConstraints = problemConstraints;
	}
}
